package com.lianjia.crawler.utils;

import com.lianjia.crawler.model.HouseInfo;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.ReplaceOptions;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * MongoDB工具类，用于操作MongoDB数据库
 */
public class MongoDBUtils {
    private static final Logger logger = LoggerFactory.getLogger(MongoDBUtils.class);
    private static MongoClient mongoClient;
    private static MongoDatabase database;
    private static final String DB_NAME = "lianjia_crawler";
    private static final String COLLECTION_NAME = "house_info";
    
    /**
     * 初始化MongoDB连接
     * 
     * @param connectionString MongoDB连接字符串
     * @param recreateCollection 是否重新创建集合（删除旧集合）
     */
    public static void init(String connectionString, boolean recreateCollection) {
        try {
            // 创建MongoDB客户端
            MongoClientSettings settings = MongoClientSettings.builder()
                    .applyConnectionString(new ConnectionString(connectionString))
                    .build();
            mongoClient = MongoClients.create(settings);
            database = mongoClient.getDatabase(DB_NAME);
            
            // 如果需要重新创建集合
            if (recreateCollection) {
                recreateCollection();
            } else {
                // 创建集合（如果不存在）
                if (!collectionExists(COLLECTION_NAME)) {
                    database.createCollection(COLLECTION_NAME);
                    logger.info("集合 {} 已创建", COLLECTION_NAME);
                    
                    // 创建索引
                    createIndexes();
                }
            }
            
            logger.info("MongoDB连接初始化成功");
        } catch (Exception e) {
            logger.error("MongoDB连接初始化失败: {}", e.getMessage(), e);
            throw new RuntimeException("MongoDB连接初始化失败", e);
        }
    }
    
    /**
     * 初始化MongoDB连接（默认不重新创建集合）
     * 
     * @param connectionString MongoDB连接字符串
     */
    public static void init(String connectionString) {
        init(connectionString, false);
    }
    
    /**
     * 删除并重新创建集合
     */
    private static void recreateCollection() {
        try {
            // 如果集合存在，则删除
            if (collectionExists(COLLECTION_NAME)) {
                database.getCollection(COLLECTION_NAME).drop();
                logger.info("集合 {} 已删除", COLLECTION_NAME);
            }
            
            // 创建新集合
            database.createCollection(COLLECTION_NAME);
            logger.info("集合 {} 已重新创建", COLLECTION_NAME);
            
            // 创建索引
            createIndexes();
        } catch (Exception e) {
            logger.error("重新创建集合失败: {}", e.getMessage(), e);
            throw new RuntimeException("重新创建集合失败", e);
        }
    }
    
    /**
     * 检查集合是否存在
     * 
     * @param collectionName 集合名称
     * @return 是否存在
     */
    private static boolean collectionExists(String collectionName) {
        for (String name : database.listCollectionNames()) {
            if (name.equals(collectionName)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 创建索引
     */
    private static void createIndexes() {
        MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
        
        // 创建唯一索引，用于去重
        collection.createIndex(Indexes.ascending("link"), new IndexOptions().unique(true));
        logger.info("已创建唯一索引: link");
        
        // 创建其他索引，用于提高查询性能
        collection.createIndex(Indexes.ascending("cityName"));
        logger.info("已创建索引: cityName");
        
        collection.createIndex(Indexes.ascending("price.totalPrice"));
        logger.info("已创建索引: price.totalPrice");
        
        collection.createIndex(Indexes.ascending("details.areaValue"));
        logger.info("已创建索引: details.areaValue");
        
        collection.createIndex(Indexes.text("title"));
        logger.info("已创建全文索引: title");
        
        // 创建location字段的普通索引，而不是地理位置索引
        collection.createIndex(Indexes.ascending("location"));
        logger.info("已创建索引: location");
    }
    
    /**
     * 保存房屋信息到MongoDB
     * 
     * @param houseInfo 房屋信息
     * @param cityName 城市名称
     * @return 是否保存成功
     */
    public static boolean saveHouseInfo(HouseInfo houseInfo, String cityName) {
        try {
            MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
            
            // 转换为Document
            Document doc = convertToDocument(houseInfo, cityName);
            
            // 使用upsert操作，如果存在则更新，不存在则插入
            Bson filter = Filters.eq("link", houseInfo.getLink());
            ReplaceOptions options = new ReplaceOptions().upsert(true);
            collection.replaceOne(filter, doc, options);
            
            return true;
        } catch (Exception e) {
            logger.error("保存房屋信息到MongoDB失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 批量保存房屋信息到MongoDB
     * 
     * @param houseInfoList 房屋信息列表
     * @param cityName 城市名称
     * @return 成功保存的数量
     */
    public static int batchSaveHouseInfo(List<HouseInfo> houseInfoList, String cityName) {
        if (houseInfoList == null || houseInfoList.isEmpty()) {
            return 0;
        }
        
        int successCount = 0;
        for (HouseInfo houseInfo : houseInfoList) {
            if (saveHouseInfo(houseInfo, cityName)) {
                successCount++;
            }
        }
        
        logger.info("批量保存房屋信息到MongoDB，总数: {}，成功: {}", houseInfoList.size(), successCount);
        return successCount;
    }
    
    /**
     * 将HouseInfo对象转换为Document
     * 
     * @param houseInfo 房屋信息
     * @param cityName 城市名称
     * @return Document对象
     */
    private static Document convertToDocument(HouseInfo houseInfo, String cityName) {
        Document doc = new Document();
        doc.append("title", houseInfo.getTitle());
        doc.append("cityName", cityName);
        doc.append("link", houseInfo.getLink());
        doc.append("image", houseInfo.getImage());
        
        // 处理地址信息
        doc.append("location", houseInfo.getLocation());
        
        // 处理价格信息
        Document priceDoc = new Document();
        if (houseInfo.getPrice() != null) {
            HouseInfo.Price price = houseInfo.getPrice();
            // 提取数字部分，便于排序和过滤
            if (price.getTotalPrice() != null) {
                String totalPrice = price.getTotalPrice();
                priceDoc.append("totalPrice", extractNumber(totalPrice));
                priceDoc.append("totalPriceText", totalPrice);
            }
            if (price.getUnitPrice() != null) {
                String unitPrice = price.getUnitPrice();
                priceDoc.append("unitPrice", extractNumber(unitPrice));
                priceDoc.append("unitPriceText", unitPrice);
            }
        }
        doc.append("price", priceDoc);
        
        // 处理详情信息
        Document detailsDoc = new Document();
        if (houseInfo.getDetails() != null) {
            Map<String, String> details = houseInfo.getDetails();
            for (Map.Entry<String, String> entry : details.entrySet()) {
                detailsDoc.append(entry.getKey(), entry.getValue());
                
                // 提取面积的数字部分，便于排序和过滤
                if ("area".equals(entry.getKey())) {
                    detailsDoc.append("areaValue", extractNumber(entry.getValue()));
                }
            }
        }
        doc.append("details", detailsDoc);
        
        // 添加创建时间和更新时间
        doc.append("createTime", System.currentTimeMillis());
        doc.append("updateTime", System.currentTimeMillis());
        
        return doc;
    }
    
    /**
     * 从字符串中提取数字
     * 
     * @param str 包含数字的字符串
     * @return 提取的数字，如果没有则返回0
     */
    private static double extractNumber(String str) {
        if (str == null || str.isEmpty()) {
            return 0;
        }
        
        try {
            // 使用正则表达式提取数字部分
            String numStr = str.replaceAll("[^0-9.]", "");
            return Double.parseDouble(numStr);
        } catch (NumberFormatException e) {
            return 0;
        }
    }
    
    /**
     * 查询房屋信息
     * 
     * @param cityName 城市名称
     * @param minPrice 最低价格（万元）
     * @param maxPrice 最高价格（万元）
     * @param minArea 最小面积（平方米）
     * @param maxArea 最大面积（平方米）
     * @param limit 限制数量
     * @return 房屋信息列表
     */
    public static List<HouseInfo> queryHouseInfo(String cityName, Double minPrice, Double maxPrice, 
                                               Double minArea, Double maxArea, int limit) {
        List<HouseInfo> result = new ArrayList<>();
        
        try {
            MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
            
            // 构建查询条件
            List<Bson> filters = new ArrayList<>();
            
            if (cityName != null && !cityName.isEmpty()) {
                filters.add(Filters.eq("cityName", cityName));
            }
            
            if (minPrice != null) {
                filters.add(Filters.gte("price.totalPrice", minPrice));
            }
            
            if (maxPrice != null) {
                filters.add(Filters.lte("price.totalPrice", maxPrice));
            }
            
            if (minArea != null) {
                filters.add(Filters.gte("details.areaValue", minArea));
            }
            
            if (maxArea != null) {
                filters.add(Filters.lte("details.areaValue", maxArea));
            }
            
            // 组合查询条件
            Bson filter = filters.isEmpty() ? new Document() : Filters.and(filters);
            
            // 执行查询
            for (Document doc : collection.find(filter).limit(limit)) {
                HouseInfo houseInfo = convertToHouseInfo(doc);
                result.add(houseInfo);
            }
            
            logger.info("查询房屋信息，条件: {}，结果数量: {}", filter.toString(), result.size());
        } catch (Exception e) {
            logger.error("查询房屋信息失败: {}", e.getMessage(), e);
        }
        
        return result;
    }
    
    /**
     * 将Document对象转换为HouseInfo
     * 
     * @param doc Document对象
     * @return HouseInfo对象
     */
    private static HouseInfo convertToHouseInfo(Document doc) {
        HouseInfo houseInfo = new HouseInfo();
        houseInfo.setTitle(doc.getString("title"));
        houseInfo.setLocation(doc.getString("location"));
        houseInfo.setImage(doc.getString("image"));
        houseInfo.setLink(doc.getString("link"));
        
        // 处理价格信息
        Document priceDoc = (Document) doc.get("price");
        if (priceDoc != null) {
            HouseInfo.Price price = new HouseInfo.Price();
            if (priceDoc.containsKey("totalPriceText")) {
                price.setTotalPrice(priceDoc.getString("totalPriceText"));
            }
            if (priceDoc.containsKey("unitPriceText")) {
                price.setUnitPrice(priceDoc.getString("unitPriceText"));
            }
            houseInfo.setPrice(price);
        }
        
        // 处理详情信息
        Document detailsDoc = (Document) doc.get("details");
        if (detailsDoc != null) {
            Map<String, String> details = new HashMap<>();
            for (String key : detailsDoc.keySet()) {
                if (!"areaValue".equals(key)) {  // 跳过我们添加的数值字段
                    Object value = detailsDoc.get(key);
                    if (value != null) {
                        details.put(key, value.toString());
                    }
                }
            }
            houseInfo.setDetails(details);
        }
        
        return houseInfo;
    }
    
    /**
     * 关闭MongoDB连接
     */
    public static void close() {
        if (mongoClient != null) {
            mongoClient.close();
            logger.info("MongoDB连接已关闭");
        }
    }
} 